#ifndef _SRC_NET_TCP_CONNECTION_H_
#define _SRC_NET_TCP_CONNECTION_H_

#include <any>
#include <memory>
#include <string_view>

#include "src/base/utils.h"
#include "src/net/buffer.h"
#include "src/net/callback.h"
#include "src/net/inet_address.h"

struct tcp_info;

namespace fio66::net {
class Channel;
class EventLoop;
class Socket;
///
/// TCP connection, for both client and server usage.
///
/// This is an interface class, so don't expose too much details.
class TcpConnection : public std::enable_shared_from_this<TcpConnection> {
 public:
  DISALLOW_COPY_AND_ASSIGN(TcpConnection)
  /// Constructs a TcpConnection with a connected sockfd
  ///
  /// User should not create this object.
  TcpConnection(EventLoop* loop, const std::string& Name, int sockfd,
                const InetAddress& localAddr, const InetAddress& peerAddr);
  ~TcpConnection();

  EventLoop* GetLoop() const { return loop_; }
  const std::string& Name() const { return name_; }
  const InetAddress& LocalAddress() const { return local_addr_; }
  const InetAddress& PeerAddress() const { return peer_addr_; }
  bool Connected() const { return state_ == kConnected; }
  bool Disconnected() const { return state_ == kDisconnected; }
  // return true if success.
  bool GetTcpInfo(struct tcp_info*) const;
  std::string GetTcpInfoString() const;

  // void send(std::string&& message); // C++11
  void Send(const void* message, int len);
  void Send(std::string_view message);
  // void send(Buffer&& message); // C++11
  void Send(Buffer* message);  // this one will swap data
  void Shutdown();             // NOT thread safe, no simultaneous calling
  // void shutdownAndForceCloseAfter(double seconds); // NOT thread safe, no
  // simultaneous calling
  void ForceClose();
  void ForceCloseWithDelay(double seconds);
  void SetTcpNoDelay(bool on);
  // reading or not
  void StartRead();
  void StopRead();
  bool IsReading() const {
    return reading_;
  };  // NOT thread safe, may race with start/StopReadInLoop

  void SetContext(const std::any& context) { context_ = context; }

  const std::any& GetContext() const { return context_; }

  std::any* GetMutableContext() { return &context_; }

  void SetConnectionCallback(const ConnectionCallback& cb) {
    connection_callback_ = cb;
  }

  void SetMessageCallback(const MessageCallback& cb) { message_callback_ = cb; }

  void SetWriteCompleteCallback(const WriteCompleteCallback& cb) {
    write_complete_callback_ = cb;
  }

  void SetHighWaterMarkCallback(const HighWaterMarkCallback& cb,
                                size_t highWaterMark) {
    high_water_mark_callback_ = cb;
    high_water_mark_ = highWaterMark;
  }

  /// Advanced interface
  Buffer* InputBuffer() { return &input_buffer_; }

  Buffer* OutputBuffer() { return &output_buffer_; }

  /// Internal use only.
  void SetCloseCallback(const CloseCallback& cb) { close_callback_ = cb; }

  // called when TcpServer accepts a new connection
  void ConnectEstablished();  // should be called only once
  // called when TcpServer has removed me from its map
  void ConnectDestroyed();  // should be called only once

 private:
  enum StateE { kDisconnected, kConnecting, kConnected, kDisconnecting };
  void HandleRead(Timestamp receiveTime);
  void HandleWrite();
  void HandleClose();
  void HandleError();
  // void sendInLoop(std::string&& message);
  void SendInLoop(std::string_view message);
  void SendInLoop(const void* message, size_t len);
  void ShutdownInLoop();
  // void shutdownAndForceCloseInLoop(double seconds);
  void ForceCloseInLoop();
  void SetState(StateE s) { state_ = s; }
  const char* StateToString() const;
  void StartReadInLoop();
  void StopReadInLoop();

  EventLoop* loop_;
  const std::string name_;
  StateE state_;  // FIXME: use atomic variable
  bool reading_;
  // we don't expose those classes to client.
  std::unique_ptr<Socket> socket_;
  std::unique_ptr<Channel> channel_;
  const InetAddress local_addr_;
  const InetAddress peer_addr_;
  ConnectionCallback connection_callback_;
  MessageCallback message_callback_;
  WriteCompleteCallback write_complete_callback_;
  HighWaterMarkCallback high_water_mark_callback_;
  CloseCallback close_callback_;
  size_t high_water_mark_;
  Buffer input_buffer_;
  Buffer output_buffer_;  // FIXME: use list<Buffer> as output buffer.
  std::any context_;
  // FIXME: creationTime_, lastReceiveTime_
  //        bytesReceived_, bytesSent_
};

typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
}  // namespace fio66::net

#endif  // _SRC_NET_TCP_CONNECTION_H_
